﻿// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
// Modify by Fernando Cerqueira To DevPhone Toolkit

using System;
using System.Diagnostics.CodeAnalysis;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

// ReSharper disable CheckNamespace
namespace DevPhone.Controls
// ReSharper restore CheckNamespace
{
    abstract class DataSource : ILoopingSelectorDataSource
    {
        private DateTimeWrapper _selectedItem;
        private Visibility _showName = Visibility.Visible;
        private Brush _foregroundContent = (Brush)Application.Current.Resources["PhoneForegroundBrush"];

        protected DataSource()
        {
                
        }

        protected DataSource(bool showName, Brush foregroundContent)
        {
            _showName = showName?Visibility.Visible : Visibility.Collapsed;
            _foregroundContent = foregroundContent;
        }

        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Keeping existing implementation.")]
        public Visibility VisibleDayName
        {
            get { return _showName; }
            set { _showName = value; }
        }

        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Keeping existing implementation.")]
        public Brush ForegroundContent
        {
            get { return _foregroundContent; }
            set { _foregroundContent = value; }
        }
        public object GetNext(object relativeTo)
        {
            DateTime? next = GetRelativeTo(((DateTimeWrapper)relativeTo).DateTime, 1);
            return next.HasValue ? new DateTimeWrapper(next.Value, _showName == Visibility.Visible, ForegroundContent) : null;
        }

        public object GetPrevious(object relativeTo)
        {
            DateTime? next = GetRelativeTo(((DateTimeWrapper)relativeTo).DateTime, -1);
            return next.HasValue ? new DateTimeWrapper(next.Value, _showName == Visibility.Visible, ForegroundContent) : null;
        }

        protected abstract DateTime? GetRelativeTo(DateTime relativeDate, int delta);

        public object SelectedItem
        {
            get { return _selectedItem; }
            set
            {
                if (value != _selectedItem)
                {
                    var valueWrapper = (DateTimeWrapper)value;
                    if ((null == valueWrapper) || (null == _selectedItem) || (valueWrapper.DateTime != _selectedItem.DateTime))
                    {
                        if (valueWrapper != null)
                            valueWrapper.VisibleDayName = _showName;
                        if (_selectedItem != null)
                            _selectedItem.VisibleDayName = _showName;
                        object previousSelectedItem = _selectedItem;
                        _selectedItem = valueWrapper;
                        var handler = SelectionChanged;
                        if (null != handler)
                        {
                            handler(this, new SelectionChangedEventArgs(new[] { previousSelectedItem }, new object[] { _selectedItem }));
                        }
                    }
                }
            }
        }

        public event EventHandler<SelectionChangedEventArgs> SelectionChanged;
    }

    class YearDataSource : DataSource
    {
        public YearDataSource()
            : base(true, (Brush)Application.Current.Resources["PhoneForegroundBrush"])
        {
                
        }
        public YearDataSource(Brush foregroundContent)
            : base(true, foregroundContent)
        {
        }
        protected override DateTime? GetRelativeTo(DateTime relativeDate, int delta)
        {
            if ((1601 == relativeDate.Year) || (3000 == relativeDate.Year))
            {
                return null;
            }
            var nextYear = relativeDate.Year + delta;
            var nextDay = Math.Min(relativeDate.Day, DateTime.DaysInMonth(nextYear, relativeDate.Month));
            return new DateTime(nextYear, relativeDate.Month, nextDay, relativeDate.Hour, relativeDate.Minute, relativeDate.Second);
        }
    }

    class MonthDataSource : DataSource
    {
        public MonthDataSource()
            : base(true, (Brush)Application.Current.Resources["PhoneForegroundBrush"])
        {
                
        }
        public MonthDataSource(Brush foregroundContent)
            : base(true, foregroundContent)
        {
        }

        protected override DateTime? GetRelativeTo(DateTime relativeDate, int delta)
        {
            const int monthsInYear = 12;
            var nextMonth = ((monthsInYear + relativeDate.Month - 1 + delta) % monthsInYear) + 1;
            var nextDay = Math.Min(relativeDate.Day, DateTime.DaysInMonth(relativeDate.Year, nextMonth));
            return new DateTime(relativeDate.Year, nextMonth, nextDay, relativeDate.Hour, relativeDate.Minute, relativeDate.Second);
        }
    }

    class DayDataSource : DataSource
    {
        public DayDataSource()
            : base(true, (Brush)Application.Current.Resources["PhoneForegroundBrush"])
        {
                
        }
        public DayDataSource(bool showName, Brush foregroundContent):base(showName,foregroundContent)
        {
        }

        protected override DateTime? GetRelativeTo(DateTime relativeDate, int delta)
        {
            var daysInMonth = DateTime.DaysInMonth(relativeDate.Year, relativeDate.Month);
            var nextDay = ((daysInMonth + relativeDate.Day - 1 + delta) % daysInMonth) + 1;
            return new DateTime(relativeDate.Year, relativeDate.Month, nextDay, relativeDate.Hour, relativeDate.Minute, relativeDate.Second);
        }
    }

    class TwelveHourDataSource : DataSource
    {
        public TwelveHourDataSource()
            : base(true, (Brush)Application.Current.Resources["PhoneForegroundBrush"])
        {
                
        }
        public TwelveHourDataSource(Brush foregroundContent)
            : base(true, foregroundContent)
        {
        }

        protected override DateTime? GetRelativeTo(DateTime relativeDate, int delta)
        {
            const int hoursInHalfDay = 12;
            var nextHour = (hoursInHalfDay + relativeDate.Hour + delta) % hoursInHalfDay;
            nextHour += hoursInHalfDay <= relativeDate.Hour ? hoursInHalfDay : 0;
            return new DateTime(relativeDate.Year, relativeDate.Month, relativeDate.Day, nextHour, relativeDate.Minute, 0);
        }
    }

    class MinuteDataSource : DataSource
    {
        public MinuteDataSource()
            : base(true, (Brush)Application.Current.Resources["PhoneForegroundBrush"])
        {
        }
        public MinuteDataSource(Brush foregroundContent)
            : base(true, foregroundContent)
        {
        }

        protected override DateTime? GetRelativeTo(DateTime relativeDate, int delta)
        {
            const int minutesInHour = 60;
            var nextMinute = (minutesInHour + relativeDate.Minute + delta) % minutesInHour;
            return new DateTime(relativeDate.Year, relativeDate.Month, relativeDate.Day, relativeDate.Hour, nextMinute, 0);
        }
    }

    class AmPmDataSource : DataSource
    {
        public AmPmDataSource()
            : base(true, (Brush)Application.Current.Resources["PhoneForegroundBrush"])
        {
        }
        public AmPmDataSource(Brush foregroundContent)
            : base(true, foregroundContent)
        {
        }

        protected override DateTime? GetRelativeTo(DateTime relativeDate, int delta)
        {
            const int hoursInDay = 24;
            var nextHour = relativeDate.Hour + (delta * (hoursInDay / 2));
            if ((nextHour < 0) || (hoursInDay <= nextHour))
            {
                return null;
            }
            return new DateTime(relativeDate.Year, relativeDate.Month, relativeDate.Day, nextHour, relativeDate.Minute, 0);
        }
    }

    class TwentyFourHourDataSource : DataSource
    {
        public TwentyFourHourDataSource()
            : base(true, (Brush)Application.Current.Resources["PhoneForegroundBrush"])
        {
        }
        public TwentyFourHourDataSource(Brush foregroundContent)
            : base(true, foregroundContent)
        {
        }

        protected override DateTime? GetRelativeTo(DateTime relativeDate, int delta)
        {
            const int hoursInDay = 24;
            var nextHour = (hoursInDay + relativeDate.Hour + delta) % hoursInDay;
            return new DateTime(relativeDate.Year, relativeDate.Month, relativeDate.Day, nextHour, relativeDate.Minute, 0);
        }
    }
}
